/*

* TCSS 422, Project 2 (Scheduler Simulation)

* Team 9 Name: blueraccoon422

* Team 9 Group Members: Jacob Hall, Jacob Mertens, Diana Gorun, Alex Stringham

* File Name: Queue.c

* Date: May 22, 2013

*/

#include "Queue.h"
#include "Process.h"
/**
 * 	Initializes a new queue for use.
 */
Queue* new_queue(int type) {
        int rv;
        rv = pthread_mutex_init(&queue_mutex[type], NULL);
        if (rv != 0) {
                printf("Error while initializing mutex\n");
        }

        Queue* q = malloc(sizeof(*q));
        if (q == NULL) {
                fprintf(stderr, "LINE: %d, malloc() failed\n", __LINE__);
        }
        q->head = q->tail = NULL;
        q->queue_type = type;
        return q;
}


/**
 * 	Clears out an entire queue
 */
Queue* free_queue(Queue* q) {
        while (q->head != NULL) {
                remove_queue(q);
        }
        return q;
}

/**
 * 	Add to the queue based off of an items priority
 */
Queue* add_priority_queue(Queue* q, int id, int priority) {
        int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        if( q == NULL) {
                printf("Blocking Queue is not initialize");
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        }
        Node* p = malloc(1 *sizeof(*p));
        if (NULL == p) {
                fprintf(stderr, "IN %s, %s: malloc() failed\n", __FILE__, "queue_add");
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        }
        p->PID = id;
        p->priority = priority;
        p->next = NULL;
        if (NULL == q->head && NULL == q->tail) {
                /* printf("Empty queue, adding p->num: %d\n\n", p->num);  */
                q->head = q->tail = p;
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        } else if (q->head == NULL || priority > q->head->priority) {
                p->next = q->head;
                q->head = p;
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        } else {
                Node* head = q->head;
                while ( head->next != NULL && head->next->priority >= priority ) {
                        head = head->next;
                }
                p->next = head->next;
                head->next = p;

                // reset the tail of the queue.
                Node* temp = q->head;
                while (temp->next != NULL) {
                        temp = temp->next;
                        q->tail = temp;
                }

        }
        pthread_mutex_unlock(&queue_mutex[q->queue_type]);
        return q;
}

/**
 * 	 Will always return the pointer to Queue
 */

QueuePtr add_queue(QueuePtr q, int id) {
        int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        Node* p = malloc(sizeof(*p));
        if (NULL == p) {
                fprintf(stderr, "IN %s, %s: malloc() failed\n", __FILE__, "queue_add");
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        }
        p->PID = id;
        p->priority = -1;
        p->next = NULL;
        if (q == NULL) {
                printf("Queue not initialized\n");
                free(p);
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        } else if (NULL == q->head && NULL == q->tail) {
                /* printf("Empty queue, adding p->num: %d\n\n", p->num);  */
                q->head = q->tail = p;
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return q;
        } else if (NULL == q->head || NULL == q->tail) {
                fprintf(stderr,
                                "There is something seriously wrong with your assignment of head/tail to the queue\n");
                free(p);
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return NULL;
        } else {
                /* printf("queue not empty, adding element to tail\n"); */
                q->tail->next = p;
                q->tail = p;
        }
        pthread_mutex_unlock(&queue_mutex[q->queue_type]);
        return q;
}

/**
 * 	 Removes the first element in the Queue since it is FIFO
 */
Node* remove_queue(Queue* q) {
        int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        Node* h = NULL;
        Node* p = NULL;

        if (NULL == q) {
                printf("queue is empty\n");
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return NULL;
        } else if (NULL == q->head && NULL == q->tail) {
                printf("Well, queue is empty\n");
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return NULL;
        } else if (NULL == q->head || NULL == q->tail) {
                printf("There is something seriously wrong with your queue\n");
                printf("One of the head/tail is empty while other is not \n");
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return NULL;
        }
        h = q->head;
        p = h->next;
        free(h);
        q->head = p;
        if (NULL == q->head)
                q->tail = q->head; /* The element tail was pointing to is free(), so we need an update */
        pthread_mutex_unlock(&queue_mutex[q->queue_type]);
        return h;
}

/**
 * 	 Prints out every element currently in the queue
 */
void print_queue(Queue* q) {
       int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        Node* p = q->head;
       // printf("head->");
       if (q != NULL) {
    	   if(q->head != q->tail){
                while(p->PID != q->tail->PID ) {
                        //queue_print_element(p);
                		//printf("[P%i(%s)]->",p->PID,pcbTable[p->PID].process->name);
                		printf("[P%i]->",p->PID);
                        p = p->next;
                }
                printf("[P%i]",p->PID);
    	   }
    	   printf("|\n");
        }
        //if nothing in the queue.
		//printf("[P%i(%s)]->",p->PID,pcbTable[p->PID].process->name);
       // printf("tail\n");
        pthread_mutex_unlock(&queue_mutex[q->queue_type]);
}

/**
 * 	Checks to see whether the Queue is empty or not
 */
int queue_isEmpty(Queue* q) {
        int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        int isEmpty = FALSE;
        //it is empty when the head of the queue is null.
        if (q->head == NULL) {
                isEmpty = TRUE;
        }
        pthread_mutex_unlock(&queue_mutex[q->queue_type]);
        return isEmpty;
}

/**
 * 	 Returns the current size of the queue
 */
int queue_size(Queue* q) {
        int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        Node* h = NULL;
        Node* n = NULL;
        h = q->head;
        n = h->next;
        int i = 0;
        if (h == NULL) {
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return 0;
        }
        i++;
        while (n != NULL) {
                i++;
                n = n->next;
        }
        pthread_mutex_unlock(&queue_mutex[q->queue_type]);
        return i;
}

/**
 * 	 Looks at what the first element in the queue is
 */
Node* queue_peek(Queue* q){
        int rv = pthread_mutex_lock(&queue_mutex[q->queue_type]);
        if (rv != 0)
                printf("Error: fail to grab the lock \n");
        if (queue_isEmpty(q)){
                Node* temp = q->head;
                pthread_mutex_unlock(&queue_mutex[q->queue_type]);
                return temp;
        }
        return NULL;
}

/**
 * 	 Prints the element within a certain Node
 */
void queue_print_element(Node* p) {
        if (p) {
                printf("Num = %d\n", p->PID);
        } else {
                printf("Can not print NULL struct \n");
        }
}
